//
// Created by James Sangalli on 2019-07-17.
//

import Foundation
import XCTest
@testable import AlphaWallet
import AlphaWalletFoundation
import AlphaWalletTokenScript

class TokenScriptSignatureVerifierTest: XCTestCase {

    func testValidatorAPIPassed() {
        if Features.current.isAvailable(.shouldLoadTokenScriptWithFailedSignatures) {
            return
        }

        let expectation = XCTestExpectation(description: "validation passes")
        let tokenScriptSignatureVerifier = TokenScriptSignatureVerifier(baseTokenScriptFiles: BaseTokenScriptFiles(baseTokenScriptFiles: [:]), networkService: FakeNetworkService(), features: TokenScriptFeatures(), reachability: FakeReachabilityManager(false))
        // swiftlint:disable:next line_length
        let uniswapTokenTSML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><ts:token xmlns:ts=\"http://tokenscript.org/2020/06/tokenscript\" xmlns:ethereum=\"urn:ethereum:constantinople\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" custodian=\"false\" xsi:schemaLocation=\"http://tokenscript.org/2020/06/tokenscript http://tokenscript.org/2020/06/tokenscript.xsd\">\r\n    <ts:label>\r\n        <ts:string xml:lang=\"en\">Uniswap SAI V1</ts:string>\r\n    </ts:label>\r\n    <ts:contract interface=\"erc20\" name=\"uniswap\">\r\n        <ts:address network=\"1\">0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14</ts:address>     <!--mainnet-->\r\n    </ts:contract>\r\n\r\n    <ts:origins>\r\n        <!-- Define the contract which holds the token that the user will use -->\r\n        <ts:ethereum contract=\"uniswap\"/> <!-- as above ts:contract name -->\r\n    </ts:origins>\r\n\r\n    <ts:cards>\r\n        <ts:card type=\"action\">\r\n            <ts:label>\r\n                <ts:string xml:lang=\"en\">About</ts:string>\r\n            </ts:label>\r\n            <ts:view xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\r\n                <style type=\"text/css\">h3 { color: #111; font-family: 'Open Sans', sans-serif; font-size: 20px; font-weight: 300; line-height: 32px; }\r\n\r\n#inputBox {\r\n  text-align: center;\r\n}\r\n\r\nhtml,\r\nbody {\r\n  height: 100%;\r\n}\r\nhtml {\r\n  font-size: 14px;\r\n}\r\nbody {\r\n  margin: 0px;\r\n  padding: 0px;\r\n  overflow-x: hidden;\r\n  min-width: 320px;\r\n  background: #FFFFFF;\r\n  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;\r\n  font-size: 14px;\r\n  line-height: 1.4285em;\r\n  color: rgba(0, 0, 0, 0.87);\r\n  font-smoothing: antialiased;\r\n}\r\n.ui.container {\r\n  display: block;\r\n  max-width: 100% !important;\r\n}\r\n@media only screen and (max-width: 767px) {\r\n  .ui.container {\r\n    width: auto !important;\r\n    margin-left: 1em !important;\r\n    margin-right: 1em !important;\r\n  }\r\n}\r\n@media only screen and (min-width: 768px) and (max-width: 991px) {\r\n  .ui.container {\r\n    width: 723px;\r\n    margin-left: auto !important;\r\n    margin-right: auto !important;\r\n  }\r\n}\r\n@media only screen and (min-width: 992px) and (max-width: 1199px) {\r\n  .ui.container {\r\n    width: 933px;\r\n    margin-left: auto !important;\r\n    margin-right: auto !important;\r\n  }\r\n}\r\n@media only screen and (min-width: 1200px) {\r\n  .ui.container {\r\n    width: 1127px;\r\n    margin-left: auto !important;\r\n    margin-right: auto !important;\r\n  }\r\n}\r\n.ui.segment {\r\n  position: relative;\r\n  background: #FFFFFF;\r\n  -webkit-box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15);\r\n  box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15);\r\n  margin: 0.5rem 0em;\r\n  padding: 0.5em 0.5em;\r\n  border-radius: 0.28571429rem;\r\n  border: 1px solid rgba(34, 36, 38, 0.15);\r\n  text-align: center;\r\n}\r\n.ui.segment:first-child {\r\n  margin-top: 0em;\r\n}\r\n.ui.segment:last-child {\r\n  margin-bottom: 0em;\r\n}\r\ninput {\r\n  position: relative;\r\n  font-weight: normal;\r\n  font-style: normal;\r\n  font-size: 12px;\r\n  display: -ms-inline-flexbox;\r\n  display: inline-flex;\r\n  color: rgba(0, 0, 0, 0.87);\r\n  padding: 9.5px 14px;\r\n  width: 300px;\r\n  border-color: #D8D8D8;\r\n}\r\ninput[type=text]:focus {\r\n  border-color: #D8D8D8;\r\n  background: #FAFAFA;\r\n  color: rgba(0, 0, 0, 0.87);\r\n  -webkit-box-shadow: none;\r\n  box-shadow: none;\r\n}\r\nlabel {\r\n  font-size: 12px;\r\n  font-weight: 500;\r\n  margin-top: 6px;\r\n}</style>\r\n                <body><script type=\"text/javascript\">\r\nclass Token {\r\n\r\n    constructor(tokenInstance) {\r\n        this.props = tokenInstance;\r\n        this.setConfirm();\r\n    }\r\n\r\n    setConfirm() {\r\n        window.onConfirm = function() {\r\n            window.close();\r\n        }\r\n    }\r\n\r\n    render() {\r\n        let message = this.props.name + \" is a token which represents your share of the liquidity pool inside uniswap and\" +\r\n            \" entitles you to a proportional percentage of the 0.3% fee for each trade of \" + this.props.name.replace(\"Uniswap\", \"\").replace(\"V1\", \"\") + \" &amp; ETH\";\r\n        return`\r\n        &lt;div class=\"ui container\"&gt;\r\n          &lt;div class=\"ui segment\"&gt;\r\n            &lt;img src=\"\"&gt;\r\n            &lt;span&gt;&lt;bold&gt;&lt;h3&gt;${message}&lt;/h3&gt;&lt;/bold&gt;&lt;/span&gt;\r\n            &lt;span&gt;&lt;bold&gt;&lt;h3&gt;&lt;strong&gt;https://uniswap.exchange/add-liquidity&lt;/strong&gt;&lt;/h3&gt;&lt;/bold&gt;&lt;/span&gt;\r\n          &lt;/div&gt;\r\n        &lt;/div&gt;\r\n`;\r\n    }\r\n}\r\n\r\nweb3.tokens.dataChanged = (oldTokens, updatedTokens) =&gt; {\r\n    const currentTokenInstance = web3.tokens.data.currentInstance;\r\n    document.getElementById('root').innerHTML = new Token(currentTokenInstance).render();\r\n};\r\n\r\n</script>\r\n<div id=\"root\"/>\r\n</body>\r\n            </ts:view>\r\n        </ts:card>\r\n    </ts:cards>\r\n        <!-- placeholder for future functions -->\r\n        <ts:attribute-type name=\"symbol\" syntax=\"1.3.6.1.4.1.1466.115.121.1.26\">\r\n            <ts:origins>\r\n                <ethereum:call as=\"utf8\" function=\"symbol\">\r\n                </ethereum:call>\r\n            </ts:origins>\r\n        </ts:attribute-type>\r\n<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n<ds:SignedInfo>\r\n<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\r\n<ds:Reference URI=\"\">\r\n<ds:Transforms>\r\n<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\r\n<ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n</ds:Transforms>\r\n<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\r\n<ds:DigestValue>utEDbVcQgJPdJl8Woj8cgjEzSgLGmq2OsooCQE+xgyE=</ds:DigestValue>\r\n</ds:Reference>\r\n</ds:SignedInfo>\r\n<ds:SignatureValue>\r\nZyfUnOJRpmDtEnBv5nhuGN9Os2uA8IRFa/onp5/tNkdaHaUSp6qOyGaO+p0q40axpCJYruDuSvz+\r\nxCzKYTaHG6WKa4o3c0GhcH2r1N4o1G4RJXXWGIBt6oZfgMs8dSstaZKU36EvBhXcPoBKnqrA+dUi\r\nFbAkyYZSjdH80JlqoCXEo+ltHgfCLPHpJzetb+wYv8xeGH9e5Wpw/n6lbSmjrtYfoy3Xdw20nEz0\r\nk01+RzdWnryyW4FBGYQqvyot0/qyn0isO6wFcXQDHcllKg53I84KWkVwhZSMIqsywKUovF22Jlsr\r\nZ67VawKBdnIPewvhiTXd/Kkxj4mO8C4BddB0NA==\r\n</ds:SignatureValue>\r\n<ds:KeyInfo>\r\n<ds:KeyName>AlphaWallet</ds:KeyName>\r\n<ds:KeyValue>\r\n<ds:RSAKeyValue>\r\n<ds:Modulus>\r\ntDHBH8jKLPEjpXsy/V9/XsXBYmc64SXx6IWG9CdJzm+iSrk3Od2ZZTS3DsR5+hp9hk6UwRyb1XKG\r\n+TMrBODfxIIWricmnhxYMqiyvwDhecm4RU4YFteekBFAsuhEGCJBtmJSrle5G3iE/9FwvTfw/cxo\r\nyAydv85OWc4UkxkfjzaXVqGGKCzSFhRm48HwG51/1nmC1mmPh070EMY4Km4N/ieJZ8egLjDAIZEI\r\nEY5Cj7ig9PPnGf2pF21/z7vm3zQViXi6XJIBn1E5CTXzDW1y1BYe0QI+dxxY0o+97mwisZu7fVfB\r\n/rJJm3g7Ye4/lITkZeRMP+OLYyG1pathItlrVQ==\r\n</ds:Modulus>\r\n<ds:Exponent>AQAB</ds:Exponent>\r\n</ds:RSAKeyValue>\r\n</ds:KeyValue>\r\n<ds:X509Data>\r\n<ds:X509Certificate>\r\nMIIFUDCCBDigAwIBAgISAy095xt55U0Cc0++zYon7TT0MA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV\r\nBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1\r\ndGhvcml0eSBYMzAeFw0xOTA5MjQxMTA1MTdaFw0xOTEyMjMxMTA1MTdaMBMxETAPBgNVBAMMCCou\r\nYXcuYXBwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDHBH8jKLPEjpXsy/V9/XsXB\r\nYmc64SXx6IWG9CdJzm+iSrk3Od2ZZTS3DsR5+hp9hk6UwRyb1XKG+TMrBODfxIIWricmnhxYMqiy\r\nvwDhecm4RU4YFteekBFAsuhEGCJBtmJSrle5G3iE/9FwvTfw/cxoyAydv85OWc4UkxkfjzaXVqGG\r\nKCzSFhRm48HwG51/1nmC1mmPh070EMY4Km4N/ieJZ8egLjDAIZEIEY5Cj7ig9PPnGf2pF21/z7vm\r\n3zQViXi6XJIBn1E5CTXzDW1y1BYe0QI+dxxY0o+97mwisZu7fVfB/rJJm3g7Ye4/lITkZeRMP+OL\r\nYyG1pathItlrVQIDAQABo4ICZTCCAmEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF\r\nBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRaKDKFkPztPgQee3418kofZddy\r\nMTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYIKwYB\r\nBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYIKwYBBQUHMAKGI2h0\r\ndHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBsGA1UdEQQUMBKCCCouYXcuYXBwggZh\r\ndy5hcHAwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYa\r\naHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdwB0ftqD\r\nMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAW1jKVxwAAAEAwBIMEYCIQCHuWOuAiqXC7Bn\r\nnYLS4BDrOrVeObYC7zcQvru7Aqx5DAIhAP9ukUp9nnQBWgAgTdfK+GdhHxboIaxJz7456ws6myft\r\nAHUAY/Lbzeg7zCzPC3KEJ1drM6SNYXePvXWmOLHHaFRL2I0AAAFtYyladwAABAMARjBEAiBaxydV\r\nJMY5/hQyrjaMMonJgQBhEKBHvv4FbthX+lZfpAIga4sB0hDaoT4knZfVhVP/u/Uv47t6z1+TEWnL\r\n/TresTwwDQYJKoZIhvcNAQELBQADggEBAGSPK0ivDprmvO72TVLZsuk/JDhCmXQcYe6cRGPiX7WL\r\nc0B6wfLaxb0rrQdmGpTiTEHS6wEa6tOMEsfxutPWeOxlqFPU97QHhLrdBlf4IDfk8i1Em3rpPPQu\r\n3M2u5nfeRXvsIxyB5vLQvuR/NwCQqA2bwkCrlLz7dr1iGem35DGI0ikIkdODTPI+RpwHK8b2iApA\r\nw6XaVGA99eCJS2dHqeyHPAc1Yf+Klv+z0FyM38ZUfazRRIQ17LtolM1U/9Ynld20SXtCrIFbcmbo\r\nw3piXuHLlDRcRxWqdL33yPoTEbPuLtS6vqDXefYP0RiYpQHHwJz4E6q5VCbK6LgILnIyX+M=\r\n</ds:X509Certificate>\r\n<ds:X509Certificate>\r\nMIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQK\r\nExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X\r\nDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxl\r\ndCdzIEVuY3J5cHQxIzAhBgNVBAMTGkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkq\r\nhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4\r\nS0EFq6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13\r\nEiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0Z8h/pZq4UmEUEz9l6YKH\r\ny9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2P\r\nMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQAB\r\no4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEE\r\nczBxMDIGCCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7\r\nBggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9kc3Ryb290Y2F4My5w\r\nN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEw\r\nPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcCARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNy\r\neXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9P\r\nVENBWDNDUkwuY3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\nAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJouM2VcGfl96S8\r\nTihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE\r\n6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwuX4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPM\r\nTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M\r\n+X+Q7UNKEkROb3N6KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n</ds:X509Certificate>\r\n</ds:X509Data>\r\n</ds:KeyInfo>\r\n</ds:Signature></ts:token>"
        tokenScriptSignatureVerifier.verifyXMLSignatureViaAPI(xml: uniswapTokenTSML) { result in
            switch result {
            case .success(domain: let domain):
                XCTAssertEqual(domain, "*.aw.app")
            case .failed, .unknownCn:
                XCTFail("Unexpected TokenScript signature verification result")
            }
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 3)
    }

    func testValidatorAPIFailed() {
        if Features.current.isAvailable(.shouldLoadTokenScriptWithFailedSignatures) {
            return
        }

        let expectation = XCTestExpectation(description: "validation passes")
        let tokenScriptSignatureVerifier = TokenScriptSignatureVerifier(baseTokenScriptFiles: BaseTokenScriptFiles(baseTokenScriptFiles: [:]), networkService: FakeNetworkService(), features: TokenScriptFeatures(), reachability: FakeReachabilityManager(false))

        // swiftlint:disable:next line_length
        let entryTokenXMLSignedWithInvalidKey = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><ts:token xmlns:ts=\"http://tokenscript.org/2019/05/tokenscript\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" custodian=\"false\" xsi:schemaLocation=\"http://tokenscript.org/2019/05/tokenscript ../../schema/tokenscript.xsd\">\n  <ts:name>\n    <ts:plurals xml:lang=\"en\">\n      <ts:string quantity=\"one\">Ticket</ts:string>\n      <ts:string quantity=\"other\">Tickets</ts:string>\n    </ts:plurals>\n    <ts:plurals xml:lang=\"es\">\n      <ts:string quantity=\"one\">Boleto de admisión</ts:string>\n      <ts:string quantity=\"other\">Boleto de admisiónes</ts:string>\n    </ts:plurals>\n    <ts:plurals xml:lang=\"zh\">\n      <ts:string quantity=\"one\">入場券</ts:string>\n      <ts:string quantity=\"other\">入場券</ts:string>\n    </ts:plurals>\n  </ts:name>\n  <ts:contract interface=\"erc875\" name=\"EntryToken\">\n    <ts:address network=\"1\">0x63cCEF733a093E5Bd773b41C96D3eCE361464942</ts:address>\n    <ts:address network=\"3\">0xFB82A5a2922A249f32222316b9D1F5cbD3838678</ts:address>\n    <ts:address network=\"4\">0x7c81DF31BB2f54f03A56Ab25c952bF3Fa39bDF46</ts:address>\n    <ts:address network=\"42\">0x2B58A9403396463404c2e397DBF37c5EcCAb43e5</ts:address>\n  </ts:contract>\n  <ts:origins>\n    <!-- Define the contract which holds the token that the user will use -->\n    <ts:ethereum contract=\"EntryToken\">\n    </ts:ethereum>\n  </ts:origins>\n  <ts:selections>\n    <ts:selection id=\"expired\">\n      <ts:name>\n        <ts:plurals xml:lang=\"en\">\n          <ts:string quantity=\"one\">Expired Ticket</ts:string>\n          <ts:string quantity=\"other\">Expired Tickets</ts:string>\n        </ts:plurals>\n        <ts:string xml:lang=\"zh\">已经过期的票</ts:string>\n      </ts:name>\n      <ts:filter>expired</ts:filter>\n    </ts:selection>\n  </ts:selections>\n  <ts:cards>\n    <ts:token>\n      <style type=\"text/css\">.ts-count {\n  font-family: \"SourceSansPro\";\n  font-weight: bolder;\n  font-size: 21px;\n  color: rgb(117, 185, 67);\n}\n.ts-category {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 21px;\n  color: rgb(67, 67, 67);\n}\n.ts-venue {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 16px;\n  color: rgb(67, 67, 67);\n}\n.ts-date {\n  font-family: \"SourceSansPro\";\n  font-weight: bold;\n  font-size: 14px;\n  color: rgb(112, 112, 112);\n  margin-left: 7px;\n  margin-right: 7px;\n}\n.ts-time {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 16px;\n  color: rgb(112, 112, 112);\n}\nhtml {\n}\n\nbody {\npadding: 0px;\nmargin: 0px;\n}\n\ndiv {\nmargin: 0px;\npadding: 0px;\n}\n\n.data-icon {\nheight:16px;\nvertical-align: middle\n}\n\n.tbml-count {   font-family: \"SourceSansPro\";   font-weight: bolder;   font-size: 21px;   color: rgb(117, 185, 67); } .tbml-category {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 21px;   color: rgb(67, 67, 67); } .tbml-venue {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 16px;   color: rgb(67, 67, 67); } .tbml-date {   font-family: \"SourceSansPro\";   font-weight: bold;   font-size: 14px;   color: rgb(112, 112, 112);   margin-left: 7px;   margin-right: 7px; } .tbml-time {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 16px;   color: rgb(112, 112, 112); }    html {    }        body {    padding: 0px;    margin: 0px;    }        div {    margin: 0px;    padding: 0px;    }     .data-icon {    height:16px;    vertical-align: middle    } \n\n\n</style>\n      <ts:view-iconified xml:lang=\"en\">\n        <div>\n    <!-- Iconified view displayed on the first page when clicking on token card in AlphaWallet -->\n    <p>Enter Satoshi's villa with this special token!\n        <img alt=\"\" src=\"\"/>\n    </p>\n</div>\n\n      </ts:view-iconified>\n      <ts:view xml:lang=\"en\">\n        <script type=\"text/javascript\">\n    (function() {\n        'use strict'\n        function GeneralizedTime(generalizedTime) {\n            this.rawData = generalizedTime;\n        }\n\n        GeneralizedTime.prototype.getYear = function () {\n            return parseInt(this.rawData.substring(0, 4), 10);\n        };\n\n        GeneralizedTime.prototype.getMonth = function () {\n            return parseInt(this.rawData.substring(4, 6), 10) - 1;\n        };\n\n        GeneralizedTime.prototype.getDay = function () {\n            return parseInt(this.rawData.substring(6, 8), 10)\n        };\n\n        GeneralizedTime.prototype.getHours = function () {\n            return parseInt(this.rawData.substring(8, 10), 10)\n        };\n\n        GeneralizedTime.prototype.getMinutes = function () {\n            var minutes = parseInt(this.rawData.substring(10, 12), 10)\n            if (minutes) return minutes\n            return 0\n        };\n\n        GeneralizedTime.prototype.getSeconds = function () {\n            var seconds = parseInt(this.rawData.substring(12, 14), 10)\n            if (seconds) return seconds\n            return 0\n        };\n\n        GeneralizedTime.prototype.getMilliseconds = function () {\n            var startIdx\n            if (time.indexOf('.') !== -1) {\n                startIdx = this.rawData.indexOf('.') + 1\n            } else if (time.indexOf(',') !== -1) {\n                startIdx = this.rawData.indexOf(',') + 1\n            } else {\n                return 0\n            }\n\n            var stopIdx = time.length - 1\n            var fraction = '0' + '.' + time.substring(startIdx, stopIdx)\n            var ms = parseFloat(fraction) * 1000\n            return ms\n        };\n\n        GeneralizedTime.prototype.getTimeZone = function () {\n            let time = this.rawData;\n            var length = time.length\n            var symbolIdx\n            if (time.charAt(length - 1 ) === 'Z'){\n                return 0\n            }\n            if (time.indexOf('+') !== -1) {\n                symbolIdx = time.indexOf('+')\n            } else if (time.indexOf('-') !== -1) {\n                symbolIdx = time.indexOf('-')\n            } else {\n                return NaN\n            }\n            var minutes = time.substring(symbolIdx + 2)\n            var hours = time.substring(symbolIdx + 1, symbolIdx + 2)\n            var one = (time.charAt(symbolIdx) === '+') ? 1 : -1\n            var intHr = one * parseInt(hours, 10) * 60 * 60 * 1000\n            var intMin = one * parseInt(minutes, 10) * 60 * 1000\n            var ms = minutes ? intHr + intMin : intHr\n            return ms\n        };\n\n        if (typeof exports === 'object') {\n            module.exports = GeneralizedTime\n        } else if (typeof define === 'function') {\n            define(GeneralizedTime)\n        } else {\n            window.GeneralizedTime = GeneralizedTime\n        }\n    }())\n</script>\n<script type=\"text/javascript\">\nclass Token {\n    constructor(tokenInstance) {\n        this.props = tokenInstance;\n    }\n\n    formatGeneralizedTimeToDate(str) {\n        const d = new GeneralizedTime(str);\n        return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleDateString();\n    }\n\n    formatGeneralizedTimeToTime(str) {\n        const d = new GeneralizedTime(str);\n        return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});\n    }\n\n    render() {\n        let time;\n        let date;\n        if (this.props.time == null) {\n            time = \"\";\n            date = \"\";\n        } else {\n            time = this.formatGeneralizedTimeToTime(this.props.time.generalizedTime);\n            date = this.props.time == null ? \"\": this.formatGeneralizedTimeToDate(this.props.time.generalizedTime);\n        }\n        return `&lt;div&gt;\n            &lt;div&gt;\n                &lt;span class=\"ts-count\"&gt;x${this.props._count}&lt;/span&gt;  &lt;span class=\"ts-category\"&gt;${this.props.name}&lt;/span&gt;\n            &lt;/div&gt;\n            &lt;div&gt;\n                &lt;span class=\"ts-venue\"&gt;${this.props.building}&lt;/span&gt;\n            &lt;/div&gt;\n                &lt;div style=\"margin: 0px; padding:0px; clear: both; height: 6px\"&gt;\n                &amp;nbsp;\n                &lt;/div&gt;\n            &lt;div&gt;\n                &lt;img src=\"\" class=\"data-icon\"/&gt;\n                &lt;span class=\"ts-date\"&gt;${date}&lt;/span&gt;\n            &lt;/div&gt;\n            &lt;div&gt;\n                &lt;span class=\"ts-time\"&gt;${time}, ${this.props.locality}&lt;/span&gt;\n            &lt;/div&gt;\n            &lt;/div&gt;`;\n    }\n}\n\nweb3.tokens.dataChanged = (old, updated, tokenCardId) =&gt; {\n    const currentTokenInstance = web3.tokens.data.currentInstance;\n    document.getElementById('root').innerHTML = new Token(currentTokenInstance).render();\n};\n\n</script>\n<div id=\"root\"/>\n\n      </ts:view>\n    </ts:token-card>\n    <ts:action>\n      <!-- this action is of the model confirm-back.\n      It should be <ts:action model=\"confirm-back\"> but Weiwu\n      shied away from specifying that due to the likely change of design causing an upgrade path issue.\n      window.onConfirm is called if user hit \"confirm\";\n      window.close() causes the back button to be pressed.\n      -->\n      <ts:name>\n        <ts:string xml:lang=\"en\">Enter</ts:string>\n        <ts:string xml:lang=\"zh\">入場</ts:string>\n        <ts:string xml:lang=\"es\">Entrar</ts:string>\n      </ts:name>\n      <ts:exclude selection=\"expired\"/>\n      <style type=\"text/css\">.ts-count {\n  font-family: \"SourceSansPro\";\n  font-weight: bolder;\n  font-size: 21px;\n  color: rgb(117, 185, 67);\n}\n.ts-category {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 21px;\n  color: rgb(67, 67, 67);\n}\n.ts-venue {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 16px;\n  color: rgb(67, 67, 67);\n}\n.ts-date {\n  font-family: \"SourceSansPro\";\n  font-weight: bold;\n  font-size: 14px;\n  color: rgb(112, 112, 112);\n  margin-left: 7px;\n  margin-right: 7px;\n}\n.ts-time {\n  font-family: \"SourceSansPro\";\n  font-weight: lighter;\n  font-size: 16px;\n  color: rgb(112, 112, 112);\n}\nhtml {\n}\n\nbody {\npadding: 0px;\nmargin: 0px;\n}\n\ndiv {\nmargin: 0px;\npadding: 0px;\n}\n\n.data-icon {\nheight:16px;\nvertical-align: middle\n}\n\n.tbml-count {   font-family: \"SourceSansPro\";   font-weight: bolder;   font-size: 21px;   color: rgb(117, 185, 67); } .tbml-category {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 21px;   color: rgb(67, 67, 67); } .tbml-venue {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 16px;   color: rgb(67, 67, 67); } .tbml-date {   font-family: \"SourceSansPro\";   font-weight: bold;   font-size: 14px;   color: rgb(112, 112, 112);   margin-left: 7px;   margin-right: 7px; } .tbml-time {   font-family: \"SourceSansPro\";   font-weight: lighter;   font-size: 16px;   color: rgb(112, 112, 112); }    html {    }        body {    padding: 0px;    margin: 0px;    }        div {    margin: 0px;    padding: 0px;    }     .data-icon {    height:16px;    vertical-align: middle    } \n\n\n</style>\n      <ts:view xml:lang=\"en\">\n        <script type=\"text/javascript\">\nclass Token {\n    constructor(tokenInstance) {\n        this.props = tokenInstance\n        document.getElementById(\"contractAddress\").value = this.props.EntryToken;\n    }\n}\n\nweb3.tokens.dataChanged = (old, updated, tokenCardId) =&gt; {\n    const currentTokenInstance = web3.tokens.data.currentInstance;\n    document.getElementById('root').innerHTML = new Token(currentTokenInstance).render();\n};\n\n</script>\n<script type=\"text/javascript\">\n    document.addEventListener(\"DOMContentLoaded\", function() {\n        window.onload = function startup() {\n            // 1. call API to fetch challenge\n            fetch('http://stormbird.duckdns.org:8080/api/getChallenge')\n                .then(function (response) {\n                    return response.text()\n                })\n                .then(function (response) {\n                    document.getElementById('msg').innerHTML = 'Challenge: ' + response\n                    window.challenge = response\n                })\n        }\n\n        window.onConfirm = function onConfirm(signature) {\n            if (window.challenge === undefined || window.challenge.length == 0) return\n            const challenge = window.challenge\n            document.getElementById('status').innerHTML = 'Wait for signature...'\n            // 2. sign challenge to generate response\n            web3.personal.sign({ data: challenge }, function (error, value) {\n                if (error != null) {\n                    document.getElementById('status').innerHTML = error\n                    window.onload();\n                    return\n                }\n\n                document.getElementById('status').innerHTML = 'Verifying credentials ...'\n                // 3. open door\n                let contractAddress = document.getElementById(\"contractAddress\").textContent;\n                fetch(`http://stormbird.duckdns.org:8080/api/checkSignature?contract=${contractAddress}&amp;challenge=${challenge}&amp;sig=${value}`)\n                    .then(function (response) {\n                        return response.text()\n                    })\n                    .then(function (response) {\n                        if (response == \"pass\") {\n                            document.getElementById('status').innerHTML = 'Entrance granted!'\n                            window.close()\n                        } else {\n                            document.getElementById('status').innerHTML = 'Failed with: ' + response\n                        }\n                    })\n            });\n            window.challenge = '';\n            document.getElementById('msg').innerHTML = '';\n        }\n    })\n</script>\n<h3>Welcome to Craig Wright's house!</h3>\n<div id=\"msg\">Preparing to unlock the entrance door.</div>\n<div id=\"contractAddress\"/>\n<div id=\"status\"/>\n\n      </ts:view>\n    </ts:action>\n  </ts:cards>\n  <ts:attribute-types>\n    <ts:attribute-type id=\"locality\" syntax=\"1.3.6.1.4.1.1466.115.121.1.15\">\n      <ts:origins>\n        <ts:ethereum as=\"utf8\" contract=\"EntryToken\" function=\"getLocality\">\n            <ts:data>\n              <ts:uint256 ref=\"tokenId\"/>\n            </ts:data>\n        </ts:ethereum>\n      </ts:origins>\n\n    </ts:attribute-type>\n    <ts:attribute-type id=\"time\" syntax=\"1.3.6.1.4.1.1466.115.121.1.24\">\n      <ts:name>\n        <ts:string xml:lang=\"en\">Time</ts:string>\n        <ts:string xml:lang=\"zh\">时间</ts:string>\n      </ts:name>\n      <ts:origins>\n        <ts:token-id as=\"utf8\" bitmask=\"0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000\"/>\n      </ts:origins>\n    </ts:attribute-type>\n    <ts:attribute-type id=\"expired\" syntax=\"1.3.6.1.4.1.1466.115.121.1.7\"> <!-- boolean -->\n      <ts:origins>\n        <ts:ethereum as=\"bool\" contract=\"EntryToken\" function=\"isExpired\">\n          <ts:data>\n            <ts:uint256 ref=\"tokenId\"/>\n          </ts:data>\n        </ts:ethereum>\n      </ts:origins>\n    </ts:attribute-type>\n    <ts:attribute-type id=\"street\" syntax=\"1.3.6.1.4.1.1466.115.121.1.15\"> <!-- string -->\n      <ts:origins>\n        <ts:ethereum as=\"utf8\" contract=\"EntryToken\" function=\"getStreet\">\n          <ts:data>\n            <ts:uint256 ref=\"tokenId\"/>\n          </ts:data>\n        </ts:ethereum>\n      </ts:origins>\n    </ts:attribute-type>\n    <ts:attribute-type id=\"building\" syntax=\"1.3.6.1.4.1.1466.115.121.1.15\"> <!-- string -->\n      <ts:origins>\n        <ts:ethereum as=\"utf8\" contract=\"EntryToken\" function=\"getBuildingName\">\n          <ts:data>\n            <ts:uint256 ref=\"tokenId\"/>\n          </ts:data>\n        </ts:ethereum>\n      </ts:origins>\n    </ts:attribute-type>\n    <ts:attribute-type id=\"state\" syntax=\"1.3.6.1.4.1.1466.115.121.1.15\"> <!-- string -->\n      <ts:origins>\n        <ts:ethereum as=\"utf8\" contract=\"EntryToken\" function=\"getState\">\n          <ts:data>\n            <ts:uint256 ref=\"tokenId\"/>\n          </ts:data>\n        </ts:ethereum>\n      </ts:origins>\n    </ts:attribute-type>\n</ts:attribute-types>\n<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\n<ds:Reference URI=\"\">\n<ds:Transforms>\n<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\n<ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n</ds:Transforms>\n<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\n<ds:DigestValue>z+I6NxdALVtlc3TuUo2QEeV9rwyAmKB4UtQWkTLQhpE=</ds:DigestValue>\n</ds:Reference>\n</ds:SignedInfo>\n<ds:SignatureValue>\nE9H57w4vE8dP/7gZYMLm4z+AohkuIWgvqiGfCnMmy87T25Q1utzlFHE71isNvdkBooBHKnFaLm4X\n7HRSdjNyNt8UIqp8ekJZt4zK/GyUZ9Fk290A4Bey6YEko2atQSCq1wRKRbbhbyT1rggYdSVfGc85\nbQ2fnLzLETITeo5Cbp6ShcWUhdPqCSSFOoIKoIpIJG/lXNa/IFUWtBs07K6S5G77O/mPXkugzpjw\nASDyefe7Z2FPgHyebrOkyDHaS3ycnqJB11r0I4ve5j9cVsjarxfRzyGzbZri/qvbr7ov3J0HDXyl\nIQ/01ct32V6UIXlqPgsk12IcqtY16BwQalIx0A==\n</ds:SignatureValue>\n<ds:KeyInfo>\n<ds:KeyName>Shong Wang</ds:KeyName>\n<ds:KeyValue>\n<ds:RSAKeyValue>\n<ds:Modulus>\nql1jleB5eU+FiIK50QwMc1pS6ieMxqciwdYHdOalEyLZZ0Swt+3Ls24g1Ktar+aJJjSJO8OjUNse\nBqECPSP2rGYIfNN7echgXEJtr9MZ5vz/G+zEyZB4lfJaBb3iGreQerdgktcjla6PC/bSiFrWs9zA\ntMp1uowMWV2T5LY7N1SYw6uLP3c8CYog7u14zwHrVgWt8MeKfgSznXyVb3A9REYo7qqnAsJq4zfR\nUfM1KQeobcLZVaPSafK6BRn0tGDoxX76ffwILRUxtMg6Ivh/OYT5cOihKhVWFRrl1/2wLoMSb+hY\nnui97tkJyEVV79c/sR1moMcsqDyaXsHc/Mz0aQ==\n</ds:Modulus>\n<ds:Exponent>AQAB</ds:Exponent>\n</ds:RSAKeyValue>\n</ds:KeyValue>\n<ds:X509Data>\n<ds:X509Certificate>\nMIIE/DCCAuSgAwIBAgIDFBtpMA0GCSqGSIb3DQEBCwUAMHkxEDAOBgNVBAoTB1Jvb3QgQ0ExHjAc\nBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1\ndGhvcml0eTEhMB8GCSqGSIb3DQEJARYSc3VwcG9ydEBjYWNlcnQub3JnMB4XDTE5MDQxNTEzNDAy\nNVoXDTE5MTAxMjEzNDAyNVowFTETMBEGA1UEAxMKc2hvbmcud2FuZzCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAKpdY5XgeXlPhYiCudEMDHNaUuonjManIsHWB3TmpRMi2WdEsLfty7Nu\nINSrWq/miSY0iTvDo1DbHgahAj0j9qxmCHzTe3nIYFxCba/TGeb8/xvsxMmQeJXyWgW94hq3kHq3\nYJLXI5Wujwv20oha1rPcwLTKdbqMDFldk+S2OzdUmMOriz93PAmKIO7teM8B61YFrfDHin4Es518\nlW9wPURGKO6qpwLCauM30VHzNSkHqG3C2VWj0mnyugUZ9LRg6MV++n38CC0VMbTIOiL4fzmE+XDo\noSoVVhUa5df9sC6DEm/oWJ7ove7ZCchFVe/XP7EdZqDHLKg8ml7B3PzM9GkCAwEAAaOB8DCB7TAM\nBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIDqDA0BgNVHSUELTArBggrBgEFBQcDAgYIKwYBBQUH\nAwEGCWCGSAGG+EIEAQYKKwYBBAGCNwoDAzAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0\ndHA6Ly9vY3NwLmNhY2VydC5vcmcvMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuY2FjZXJ0\nLm9yZy9yZXZva2UuY3JsMC8GA1UdEQQoMCaCCnNob25nLndhbmegGAYIKwYBBQUHCAWgDAwKc2hv\nbmcud2FuZzANBgkqhkiG9w0BAQsFAAOCAgEAMDUSBLJIbJWlq7nkyMIvyOsb7LvFGsTxh5LzK9qU\n0rGwKRbArMddz/n23N6FXDr0QwFw1lrkI5dmJYOawAUF7XfXcscO2QmgerQnRWBFa2yFI4J5ML3V\n986frNEho+JzNY0cj5JTDsyll6l4hRUvJuYOpqDc2tE3nCw6zu1caWCbWI5oC5+1CgdKznEqaMbr\nGH4Ao2ah/EJuk/F4GXok6dmzLY5c6PpElv2n+tvXc4Xbj1qatflcqk3dxkjJXVU/ebRGSl/vvkm8\nPkB/g6d9Tq48twsjqEzr8wsTrDr/+suS4JBMjYT66PyNNEtsWClJ/jYdXOedRlFLdJlkNe3gVb+9\nUYJ9/StAy+8dlRH+7Q/bBPrt2GzI3fgT7LKEKtTsVhS1+XrljXqh5BZdrZEwYlyROQ4yR+doBp7E\nm3MxJTzJWdI/Rm7w9cmQT5Nai6JU4oPYkya8lOvNXZveN/PYFN0Ie3PQ9lVBp/ABpgILjjQOZraD\npn/rc/F8mH3P0xc5+6hKqFXDEBTWy/PabBs3QSyua3Db25Olq2KXC6hUVt/tvRRA1QVQAbhuGhqJ\nV63WCiZw9mNP21d8YemZairuhBZ/DEDMZs9WGQMN6oaBawVCkQyk1hVPLvq/pD8jekXKYc2YYp8P\nNzwOeODqfDg+dlvdL+7R0kJ6VsTsJYwFur4=\n</ds:X509Certificate>\n<ds:X509Certificate>\nMIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290IENBMR4wHAYD\nVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNBIENlcnQgU2lnbmluZyBBdXRo\nb3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRAY2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDla\nFw0zMzAzMjkxMjI5NDlaMHkxEDAOBgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cu\nY2FjZXJ0Lm9yZzEiMCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3\nDQEJARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA\nziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ8BLPRoZzYLdufujAWGSu\nzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6zWYyN3L69wj1x81YyY7nDl7qPv4coRQK\nFWyGhFtkZip6qUtTefWIonvuLwphK42yfk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiY\nUQXw8wWRBB0bF4LsyFe7w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j3\n6NK2B5jcG8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4kepKw\nDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43qlaegw1SJpfvbi1Ei\nnbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQQUxPKZgh/TMfdQwEUfoZd9vUFBzu\ngcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivUfslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1n\nPkZPcCBnzsXWWdsC4PDSy826YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsC\nAwEAAaOCAc4wggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY\ngBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEeMBwGA1UECxMV\naHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0IFNpZ25pbmcgQXV0aG9yaXR5\nMSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2VydC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAy\nBgNVHR8EKzApMCegJaAjhiFodHRwczovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZI\nAYb4QgEEBCMWIWh0dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgE\nJxYlaHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhCAQ0ESRZH\nVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQgb3ZlciB0byBodHRwOi8v\nd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIBACjH7pyCArpcgBLKNQodgW+JapnM8mgP\nf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCcnWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT\n6ij0rPtmlVOKTV39O9lg18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+\nTyGpkO/cgr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBlJzt7\nu0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvYsONvRUgzEv/+PDIq\nVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+TSCX8Ev2fQtzzxD72V7DX3WnRBnc0\nCkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYFCpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5\nhvk9C8JzC6WZrG/8Z7jlLwumGCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1Yj\nqrZslMZIBjzkzk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW\nomTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD\n</ds:X509Certificate>\n</ds:X509Data>\n</ds:KeyInfo>\n</ds:Signature></ts:token>"
        tokenScriptSignatureVerifier.verifyXMLSignatureViaAPI(xml: entryTokenXMLSignedWithInvalidKey) { result in
            switch result {
            case .failed:
                break
            case .success, .unknownCn:
                XCTFail("Unexpected TokenScript signature verification result")
            }
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 3)
    }
}
